package io.gitee.seen.core.util;


import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * 字符串到日期类型，为解析parse
 * 日期类型到字符串，为格式format
 *
 * @author chixuehui
 * @since 1.8
 */
public class DateUtils {

    public static final String YYYYMMDD = "yyyyMMdd";

    public static final String HHMMSS = "HHmmss";

    private DateUtils() {
    }

    /**
     * 传入字符串格式返回当前时间
     *
     * @param pattern 合法的时间格式
     * @return String 当前时间字符串
     */
    public static String now(String pattern) {

        LocalDateTime dateTime = LocalDateTime.now();
        return format(dateTime, pattern);
    }

    /**
     * 获取当前yyyyMMdd格式的日期
     *
     * @return String 当日
     */
    public static String currentDate() {

        return now(YYYYMMDD);
    }

    /**
     * 获取当前HHmmss格式的时间
     *
     * @return 当前时间
     */
    public static String currentTime() {

        return now(HHMMSS);
    }

    /**
     * 按照指定标准转换为date
     *
     * @param dateTimeStr 时间字符串
     * @param pattern     时间格式字符串
     * @return date
     * @throws ParseException 解析异常
     */
    public static Date parse(String dateTimeStr, String pattern) throws ParseException {

        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        return dateFormat.parse(dateTimeStr);
    }

    /**
     * Date转LocalDateTime，使用默认时区
     *
     * @param date Date
     * @return LocalDateTime
     */
    public static LocalDateTime convert(Date date) {

        ZoneId zoneId = ZoneId.systemDefault();
        Instant instant = date.toInstant();
        return instant.atZone(zoneId).toLocalDateTime();
    }

    /**
     * LocalDateTime转Date，使用默认时区
     *
     * @param localDateTime LocalDateTime
     * @return Date
     */
    public static Date convert(LocalDateTime localDateTime) {

        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        return Date.from(zdt.toInstant());
    }

    /**
     * long转LocalDateTime
     *
     * @param timestamp 时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime convert(long timestamp) {

        return convert(timestamp, null);
    }

    /**
     * 将long通过指定ZoneId转为LocalDateTime
     *
     * @param timestamp long，时间戳
     * @param zone      ZoneId
     * @return LocalDateTime 时间
     */
    public static LocalDateTime convert(long timestamp, ZoneId zone) {

        if (null == zone) {
            zone = ZoneId.systemDefault();
        }
        Instant instant = Instant.ofEpochMilli(timestamp);
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 判断是否在某个时间段之间（闭区间）
     *
     * @param localtime 目标时间
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return true，是在starttime和endtime之间（闭区间）
     */
    public static boolean between(LocalTime localtime, LocalTime startTime, LocalTime endTime) {

        return localtime.compareTo(endTime) <= 0 && localtime.compareTo(startTime) >= 0;
    }

    /**
     * 日期转换为字符串格式
     *
     * @param localDate 日期
     * @return yyyyMMdd格式
     */
    public static String getBasicIsoDate(LocalDate localDate) {

        return localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
    }

    /**
     * 日期转换为字符串格式 今日
     *
     * @return yyyyMMdd格式
     */
    public static String getBasicIsoDate() {

        return getBasicIsoDate(LocalDate.now());
    }

    /**
     * 日期转换为字符串格式
     *
     * @param localDate 日期
     * @return yyyy-MM-dd格式
     */
    public static String getIsoLocalDate(LocalDate localDate) {

        return localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
    }

    /**
     * 日期转换为字符串格式 今日
     *
     * @return yyyy-MM-dd格式
     */
    public static String getIsoLocalDate() {

        return getIsoLocalDate(LocalDate.now());
    }

    /**
     * 字符串转日期
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @return 日期
     */
    public static LocalDate formatBasicIsoDate(String yyyyMMdd) {

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.BASIC_ISO_DATE;
        return LocalDate.parse(yyyyMMdd, dateTimeFormatter);
    }

    /**
     * yyyyMMdd转yyyy-MM-dd
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @return 日期
     */
    public static String formatBasicIsoDateToLocalIsoDate(String yyyyMMdd) {

        LocalDate localDate = formatBasicIsoDate(yyyyMMdd);
        return formatIsoLocalDate(localDate);
    }

    /**
     * 获取两个指定时间的偏移量
     * 举例：
     * <p>
     * offset("20200101", "20211014", "yyyyMMdd", "年") = 1
     * </p>
     * <p>
     * offset("20210101", "20211014", "yyyyMMdd", "月") = 9
     * </p>
     * <p>
     * offset("20211014", "20211019", "yyyyMMdd", "日") = 5
     * </p>
     * <p>
     * offset("20211014 12:00:00", "20211019 12:00:00", "yyyyMMdd HH:mm:ss", "时") = 120
     * </p>
     * <p>
     * offset("20211014 12:00:00", "20211014 12:09:00", "yyyyMMdd HH:mm:ss", "分") = 9
     * </p>
     * <p>
     * offset("20211014 12:00:00", "20211014 12:01:08", "yyyyMMdd HH:mm:ss", "秒") = 68
     * </p>
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param pattern   Java时间格式。举例：yyyyMMdd、yyyy-MM-dd、yyyyMMdd HH:mm:ss
     * @param type      单位；可选：年、月、日、时、分、秒
     * @return offset 偏移量
     */
    public static long offset(String startTime, String endTime, String pattern, String type) {

        Temporal start;
        Temporal end;
        if (YYYYMMDD.equals(pattern)) {
            start = LocalDate.parse(startTime, DateTimeFormatter.ofPattern(pattern));
            end = LocalDate.parse(endTime, DateTimeFormatter.ofPattern(pattern));
        } else {
            start = LocalDateTime.parse(startTime, DateTimeFormatter.ofPattern(pattern));
            end = LocalDateTime.parse(endTime, DateTimeFormatter.ofPattern(pattern));
        }
        ChronoUnit chronoUnit;
        switch (type) {
            case "年":
                chronoUnit = ChronoUnit.YEARS;
                break;
            case "月":
                chronoUnit = ChronoUnit.MONTHS;
                break;
            case "日":
                chronoUnit = ChronoUnit.DAYS;
                break;
            case "时":
                chronoUnit = ChronoUnit.HOURS;
                break;
            case "分":
                chronoUnit = ChronoUnit.MINUTES;
                break;
            case "秒":
                chronoUnit = ChronoUnit.SECONDS;
                break;
            default:
                chronoUnit = ChronoUnit.DAYS;
        }

        return start.until(end, chronoUnit);
    }

    /**
     * 日期转换为字符串格式
     *
     * @param localDate 日期
     * @return yyyyMMdd格式
     */
    public static String formatBasicIsoDate(LocalDate localDate) {

        return localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
    }

    /**
     * 日期转换为字符串格式
     *
     * @param localDateTime 日期时间
     * @return yyyyMMdd格式
     */
    public static String formatBasicIsoDate(LocalDateTime localDateTime) {

        return localDateTime.format(DateTimeFormatter.BASIC_ISO_DATE);
    }

    /**
     * 日期转换为字符串格式 今日
     *
     * @return yyyyMMddHHmmssSSS格式
     */
    public static String formatTime() {

        return format(LocalDateTime.now(), "yyyyMMddHHmmssSSS");
    }

    /**
     * 日期转换为14位字符串格式 今日
     *
     * @param localDateTime 日期时间
     * @return yyyyMMddHHmmss格式
     */
    public static String formatTimeFourteen(LocalDateTime localDateTime) {

        return DateUtils.format(localDateTime, "yyyyMMddHHmmss");
    }

    /**
     * 日期转换为14位字符串格式 今日
     *
     * @return yyyyMMddHHmmss格式
     */
    public static String formatTimeFourteen() {

        return DateUtils.formatTimeFourteen(LocalDateTime.now());
    }

    /**
     * 日期转换为字符串格式 今日
     *
     * @return yyyyMMdd格式
     */
    public static String formatBasicIsoDate() {

        return formatBasicIsoDate(LocalDate.now());
    }

    /**
     * 日期转换为字符串格式
     *
     * @param localDate 日期
     * @return yyyy-MM-dd格式
     */
    public static String formatIsoLocalDate(LocalDate localDate) {

        return localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
    }

    /**
     * yyyyMMdd日期转换为yyyy-MM-dd格式
     *
     * @param yyyyMMdd 日期
     * @return yyyy-MM-dd格式
     */
    public static String basicIsoDateToIsoLocalDate(String yyyyMMdd) {

        return parseBasicIsoDate(yyyyMMdd).format(DateTimeFormatter.ISO_LOCAL_DATE);
    }

    /**
     * 日期时间转换为字符串格式
     *
     * @param localDateTime 日期时间
     * @param pattern       日期格式字符串
     * @return 对应日期字符串
     */
    public static String format(LocalDateTime localDateTime, String pattern) {

        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期时间转换为字符串格式
     *
     * @param localDateTime 日期时间
     * @return yyyy-MM-ddTHH:mm:ssZ格式
     */
    public static String formatTzDateTime(LocalDateTime localDateTime) {

        return format(localDateTime, "yyyy-MM-dd") + "T" + format(localDateTime, "HH:mm:ss") + "Z";
    }

    /**
     * 日期时间转换为字符串格式
     *
     * @param localDateTime     日期时间
     * @param dateTimeFormatter 日期格式字符串
     * @return 对应日期字符串
     */
    public static String format(LocalDateTime localDateTime, DateTimeFormatter dateTimeFormatter) {

        return localDateTime.format(dateTimeFormatter);
    }

    /**
     * 日期转换为字符串格式 今日
     *
     * @return yyyy-MM-dd格式
     */
    public static String formatIsoLocalDate() {

        return formatIsoLocalDate(LocalDate.now());
    }

    /**
     * Date-to-LocalDateTime
     *
     * @param date 低版本日期格式
     * @return 1.8版本日期格式
     */
    public static LocalDateTime getLocalDateTime(Date date) {

        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * Date-to-LocalDate
     *
     * @param date 低版本日期格式
     * @return 1.8版本日期格式
     */
    public static LocalDate getLocalDate(Date date) {

        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    /**
     * LocalDateTime-to-Date
     *
     * @param localDateTime 1.8版本日期格式
     * @return 低版本日期格式
     */
    public static Date getDate(LocalDateTime localDateTime) {

        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * 字符串转日期
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @return 日期
     */
    public static LocalDate parseBasicIsoDate(String yyyyMMdd) {

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.BASIC_ISO_DATE;
        return LocalDate.parse(yyyyMMdd, dateTimeFormatter);
    }

    /**
     * 字符串转日期
     *
     * @param date yyyy-MM-dd格式日期
     * @return 日期
     */
    public static LocalDate parseIsoLocalDate(String date) {

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE;
        return LocalDate.parse(date, dateTimeFormatter);
    }

    /**
     * 字符串转日期时间
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @return 日期时间
     */
    public static LocalDateTime parseBasicIsoDateTime(String yyyyMMdd) {

        return parseBasicIsoDate(yyyyMMdd).atStartOfDay();
    }

    /**
     * 年偏移
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @param offset   偏移
     * @return yyyyMMdd格式日期
     */
    public static String yearOffset(String yyyyMMdd, int offset) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate localDate1 = localDate.plusYears(offset);
        return formatBasicIsoDate(localDate1);
    }

    /**
     * 季偏移
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @param offset   偏移
     * @return yyyyMMdd格式日期
     */
    public static String quartersOffset(String yyyyMMdd, int offset) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate localDate1 = localDate.plusMonths(offset * 3L);
        return formatBasicIsoDate(localDate1);
    }

    /**
     * 月偏移
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @param offset   偏移
     * @return yyyyMMdd格式日期
     */
    public static String monthsOffset(String yyyyMMdd, int offset) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate localDate1 = localDate.plusMonths(offset);
        return formatBasicIsoDate(localDate1);
    }

    /**
     * 周偏移
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @param offset   偏移
     * @return yyyyMMdd格式日期
     */
    public static String weeksOffset(String yyyyMMdd, int offset) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate localDate1 = localDate.plusWeeks(offset);
        return formatBasicIsoDate(localDate1);
    }

    /**
     * 日偏移
     *
     * @param yyyyMMdd yyyyMMdd格式日期
     * @param offset   偏移
     * @return yyyyMMdd格式日期
     */
    public static String daysOffset(String yyyyMMdd, int offset) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate localDate1 = localDate.plusDays(offset);
        return formatBasicIsoDate(localDate1);
    }

    /**
     * 计算两个日期之间的天数
     *
     * @param firstDate   日期1在前 格式yyyyMMdd
     * @param secoundDate 日期2在后 格式yyyyMMdd
     * @return 多个日期
     */
    public static List<String> betweenDayDates(String firstDate, String secoundDate) {

        List<String> dates = new ArrayList<>();
        LocalDate localDate1 = parseBasicIsoDate(firstDate);
        LocalDate localDate2 = parseBasicIsoDate(secoundDate);
        // secoundDate >=firstDate// secoundDate>=firstDate
        for (; !localDate2.isBefore(localDate1); localDate1 = localDate1.plusDays(1)) {
            dates.add(formatBasicIsoDate(localDate1));
        }
        return dates;
    }

    /**
     * 计算两个日期之间的天数 周期为每周
     *
     * @param firstDate   日期1在前 格式yyyyMMdd
     * @param secoundDate 日期2在后 格式yyyyMMdd
     * @return 多个日期
     */
    public static List<String> betweenDayWeeks(String firstDate, String secoundDate) {

        List<String> dates = new ArrayList<>();
        LocalDate localDate1 = parseBasicIsoDate(firstDate);
        LocalDate localDate2 = parseBasicIsoDate(secoundDate);
        // secoundDate >=firstDate// secoundDate>=firstDate
        for (; !localDate2.isBefore(localDate1); localDate1 = localDate1.plusWeeks(1)) {
            dates.add(formatBasicIsoDate(localDate1));
        }
        return dates;
    }

    /**
     * 计算两个日期之间的天数 周期为每月
     *
     * @param firstDate   日期1在前 格式yyyyMMdd
     * @param secoundDate 日期2在后 格式yyyyMMdd
     * @return 多个日期
     */
    public static List<String> betweenDayMonths(String firstDate, String secoundDate) {

        List<String> dates = new ArrayList<>();
        LocalDate localDate1 = parseBasicIsoDate(firstDate);
        LocalDate localDate2 = parseBasicIsoDate(secoundDate);
        // secoundDate >=firstDate// secoundDate>=firstDate
        for (; !localDate2.isBefore(localDate1); localDate1 = localDate1.plusMonths(1)) {
            dates.add(formatBasicIsoDate(localDate1));
        }
        return dates;
    }

    /**
     * 计算两个日期之间的天数 周期为每季度
     *
     * @param firstDate   日期1在前 格式yyyyMMdd
     * @param secoundDate 日期2在后 格式yyyyMMdd
     * @return 多个日期
     */
    public static List<String> betweenDayQuarters(String firstDate, String secoundDate) {

        List<String> dates = new ArrayList<>();
        LocalDate localDate1 = parseBasicIsoDate(firstDate);
        LocalDate localDate2 = parseBasicIsoDate(secoundDate);
        // secoundDate >=firstDate// secoundDate>=firstDate
        for (; !localDate2.isBefore(localDate1); localDate1 = localDate1.plusMonths(3)) {
            dates.add(formatBasicIsoDate(localDate1));
        }
        return dates;
    }

    /**
     * 计算两个日期之间的天数 周期为每年
     *
     * @param firstDate   日期1在前 格式yyyyMMdd
     * @param secoundDate 日期2在后 格式yyyyMMdd
     * @return 多个日期
     */
    public static List<String> betweenDayYears(String firstDate, String secoundDate) {

        List<String> dates = new ArrayList<>();
        LocalDate localDate1 = parseBasicIsoDate(firstDate);
        LocalDate localDate2 = parseBasicIsoDate(secoundDate);
        // secoundDate >=firstDate// secoundDate>=firstDate
        for (; !localDate2.isBefore(localDate1); localDate1 = localDate1.plusYears(1)) {
            dates.add(formatBasicIsoDate(localDate1));
        }
        return dates;
    }

    /**
     * 取周一
     *
     * @param yyyyMMdd   格式yyyyMMdd
     * @param weeksToAdd 0 = 本周，-1 = 上一周，-2 = 上两周，1 = 下一周，2 = 下两周
     * @return 格式yyyyMMdd
     */
    public static String weekStartDate(String yyyyMMdd, long weeksToAdd) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate with = localDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
        LocalDate plusWeeks = with.plusWeeks(weeksToAdd);
        return formatBasicIsoDate(plusWeeks);
    }

    /**
     * 取本周开始日，即本周一
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String weekStartDate(String yyyyMMdd) {

        return weekStartDate(yyyyMMdd, 0);
    }

    /**
     * 取上周一
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String previousWeekStartDate(String yyyyMMdd) {

        return weekStartDate(yyyyMMdd, -1);
    }

    /**
     * 取下周一
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String nextWeekStartDate(String yyyyMMdd) {

        return weekStartDate(yyyyMMdd, 1);
    }

    /**
     * 取周日
     *
     * @param yyyyMMdd   格式yyyyMMdd
     * @param weeksToAdd 0 = 本周，-1 = 上一周，-2 = 上两周，1 = 下一周，2 = 下两周
     * @return 格式yyyyMMdd
     */
    public static String weekEndDate(String yyyyMMdd, long weeksToAdd) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate with = localDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
        LocalDate plusWeeks = with.plusWeeks(weeksToAdd);
        return formatBasicIsoDate(plusWeeks);
    }

    /**
     * 取本周结束日，即周日
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String weekEndDate(String yyyyMMdd) {

        return weekEndDate(yyyyMMdd, 0);
    }

    /**
     * 取下周日
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String nextWeekEndDate(String yyyyMMdd) {

        return weekEndDate(yyyyMMdd, 1);
    }

    /**
     * 取上周日
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String previousWeekEndDate(String yyyyMMdd) {

        return weekEndDate(yyyyMMdd, -1);
    }

    /**
     * 取月开始日
     *
     * @param yyyyMMdd    格式yyyyMMdd
     * @param monthsToAdd 0 = 本月，-1 = 上月，-2 = 上两个月，1 = 下月，2 = 下两个月
     * @return 格式yyyyMMdd
     */
    public static String monthStartDate(String yyyyMMdd, long monthsToAdd) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate with = localDate.withDayOfMonth(1).plusMonths(monthsToAdd);
        return formatBasicIsoDate(with);
    }

    /**
     * 取本月初，即本月第一日、本月开始日
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String monthStartDate(String yyyyMMdd) {

        return monthStartDate(yyyyMMdd, 0);
    }

    /**
     * 取上月初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String previousMonthStartDate(String yyyyMMdd) {

        return monthStartDate(yyyyMMdd, -1);
    }

    /**
     * 取下月初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String nextMonthStartDate(String yyyyMMdd) {

        return monthStartDate(yyyyMMdd, 1);
    }

    /**
     * 取本月结束日
     *
     * @param yyyyMMdd    格式yyyyMMdd
     * @param monthsToAdd 0 = 本月，-1 = 上月，-2 = 上两个月，1 = 下月，2 = 下两个月
     * @return 格式yyyyMMdd
     */
    public static String monthEndDate(String yyyyMMdd, long monthsToAdd) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        // 本月开始日+1个月-1天=本月结束日
        LocalDate with = localDate.withDayOfMonth(1).plusMonths(1).plusDays(-1).plusMonths(monthsToAdd);
        return formatBasicIsoDate(with);
    }

    /**
     * 取本月结束日，即本月末，本月最后一日
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String monthEndDate(String yyyyMMdd) {

        return monthEndDate(yyyyMMdd, 0);
    }

    /**
     * 取上月末
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String previousMonthEndDate(String yyyyMMdd) {

        return monthEndDate(yyyyMMdd, -1);
    }

    /**
     * 取下月末
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String nextMonthEndDate(String yyyyMMdd) {

        return monthEndDate(yyyyMMdd, 1);
    }

    /**
     * 取季度开始日，即季初
     *
     * @param yyyyMMdd    格式yyyyMMdd
     * @param quartersAdd 0 = 本季度，-1 = 上季度，-2 = 上两个季度，1 = 下季度，2 = 下两个季度
     * @return 格式yyyyMMdd
     */
    public static String quarterStartDate(String yyyyMMdd, long quartersAdd) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        // 获取月份的第一个季度月
        Month month = localDate.getMonth().firstMonthOfQuarter();
        LocalDate with = LocalDate.of(localDate.getYear(), month, 1);
        LocalDate plusMonths = with.plusMonths(3 * quartersAdd);
        return formatBasicIsoDate(plusMonths);
    }

    /**
     * 取本季度开始日，即本季初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String quarterStartDate(String yyyyMMdd) {

        return quarterStartDate(yyyyMMdd, 0);
    }

    /**
     * 取上季度开始日，即上季初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String previousQuarterStartDate(String yyyyMMdd) {

        return quarterStartDate(yyyyMMdd, -1);
    }

    /**
     * 取下季度开始日，即下季初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String nextQuarterStartDate(String yyyyMMdd) {

        return quarterStartDate(yyyyMMdd, 1);
    }

    /**
     * 取本季度结束日
     *
     * @param yyyyMMdd    格式yyyyMMdd
     * @param quartersAdd 0 = 本季度，-1 = 上季度，-2 = 上两个季度，1 = 下季度，2 = 下两个季度
     * @return 格式yyyyMMdd
     */
    public static String quarterEndDate(String yyyyMMdd, long quartersAdd) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        Month month = Month.of(localDate.getMonth().firstMonthOfQuarter().getValue() + 2);
        // 1季度月+2个月=季度末的那个月
        LocalDate of = LocalDate.of(localDate.getYear(), month, month.length(localDate.isLeapYear()));
        LocalDate plusMonths = of.plusMonths(3 * quartersAdd);
        return formatBasicIsoDate(plusMonths);
    }

    /**
     * 取本季度结束日，即本季末
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String quarterEndDate(String yyyyMMdd) {

        return quarterEndDate(yyyyMMdd, 0);
    }

    /**
     * 取上季度结束日，即上季末
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String previousQuarterEndDate(String yyyyMMdd) {

        return quarterEndDate(yyyyMMdd, -1);
    }

    /**
     * 取下季度结束日，即下季末
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String nextQuarterEndDate(String yyyyMMdd) {

        return quarterEndDate(yyyyMMdd, 1);
    }

    /**
     * 取年开始日，即年初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @param yearsAdd 0 = 今年，-1 = 去年，-2 = 前年，1 = 明年，2 = 后年
     * @return 格式yyyyMMdd
     */
    public static String yearStartDate(String yyyyMMdd, long yearsAdd) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate with = localDate.withDayOfYear(1);
        LocalDate plusYears = with.plusYears(yearsAdd);
        return formatBasicIsoDate(plusYears);
    }

    /**
     * 取今年开始日，即今年年初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String yearStartDate(String yyyyMMdd) {

        return yearStartDate(yyyyMMdd, 0);
    }

    /**
     * 取去年开始日，即去年年初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String previousYearStartDate(String yyyyMMdd) {

        return yearStartDate(yyyyMMdd, -1);
    }

    /**
     * 取明年开始日，即明年年初
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String nextYearStartDate(String yyyyMMdd) {

        return yearStartDate(yyyyMMdd, 1);
    }

    /**
     * 取年结束日，即年末
     *
     * @param yyyyMMdd   格式yyyyMMdd
     * @param yearsToAdd 0 = 今年，-1 = 去年，-2 = 前年，1 = 明年，2 = 后年
     * @return 格式yyyyMMdd
     */
    public static String yearEndDate(String yyyyMMdd, long yearsToAdd) {

        LocalDate localDate = parseBasicIsoDate(yyyyMMdd);
        LocalDate with = localDate.withDayOfYear(1).plusYears(1).plusDays(-1);
        // 本年开始日+1年-1天=本年结束日
        LocalDate plusYears = with.plusYears(yearsToAdd);
        return formatBasicIsoDate(plusYears);
    }

    /**
     * 取本年结束日，即本年年末
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String yearEndDate(String yyyyMMdd) {

        return yearEndDate(yyyyMMdd, 0);
    }

    /**
     * 取去年结束日，即去年年末
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String previousYearEndDate(String yyyyMMdd) {

        return yearEndDate(yyyyMMdd, -1);
    }

    /**
     * 取明年结束日，即明年年末
     *
     * @param yyyyMMdd 格式yyyyMMdd
     * @return 格式yyyyMMdd
     */
    public static String nextYearEndDate(String yyyyMMdd) {

        return yearEndDate(yyyyMMdd, 1);
    }

    /**
     * 第一个日期是第二个日期以后
     * isAfter(20200101 , 20200102) = false
     *
     * @param firstDate  第一个日期
     * @param secondDate 第二个日期
     * @return 以后
     */
    public static boolean isAfter(String firstDate, String secondDate) {
        LocalDate firstLocalDate = parseBasicIsoDate(firstDate);
        LocalDate secondLocalDate = parseBasicIsoDate(secondDate);
        return firstLocalDate.isAfter(secondLocalDate);
    }
}
